using MultiLang;
#region

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

#endregion

namespace Rsdn.TreeGrid
{
    public partial class TreeGrid : ListView
    {
        private const int HOffset = 4;
        private readonly AsyncOperation _uiOperation = AsyncOperationManager.CreateOperation(null);
        private ITreeNode _activeNode;
        private int _activeNodeIndex = -1;

// private const TextFormatFlags _defaultDefaultTextFormat = (TextFormatFlags)0x8004;
        private TextFormatFlags _defaultTextFormat = (TextFormatFlags) 0x8004;
        public List<ITreeNode> _displayNodes = new List<ITreeNode>(100);
        private Font _highlightFont;
        private int _indent = 5;
        private PaintCache _paintCache;
        private ITreeNode _root;

        /// <summary>
        ///   Если установить эту переменную в true и изменить значение свойства
        ///   VirtualListSize, то ListView не будет прокручивать свое содержимое.
        /// </summary>
        private bool _scrollOnSetItemCount;

        /// <summary>
        ///   Этот блокиратор, не равный 0, говорит, что в данный момент происходит
        ///   изменение индекса текущей ветки.
        /// </summary>
        private int _setActiveNodeIndexLock;

        private int _treeColumnIndex;
        private VisualStyleRenderer _visualStyleMinusRenderer;
        private VisualStyleRenderer _visualStylePlusRenderer;
        private object lockInsertNodes = new object();

        /// <summary>
        /// Initializes a new instance of the <see cref="TreeGrid"/> class.
        /// </summary>
        public TreeGrid()
        {
            _uiOperation = AsyncOperationManager.CreateOperation(null);
            base.View = View.Details;
            base.VirtualMode = true;
            base.OwnerDraw = true;

            ControlStyles controlStyles = ControlStyles.AllPaintingInWmPaint
                                          | ControlStyles.Opaque
                                          | ControlStyles.OptimizedDoubleBuffer;

            SetStyle(controlStyles, true);

            InitializeComponent();
            ItemSelectionChanged += new ListViewItemSelectionChangedEventHandler(TreeGrid_ItemSelectionChanged);
        }

        void TreeGrid_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
        }

        /// <summary>
        /// Gets or sets the active node.
        /// </summary>
        /// <value>The active node.</value>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ITreeNode ActiveNode
        {
            get
            {
                return _activeNode;
            }

            set {
                SetActiveNodeInternal(value);
            }
        }

        private void SetActiveNodeInternal(ITreeNode value)
        {
            if (InvokeRequired)
            {
                MethodInvoker method = delegate { SetActiveNodeInternal(value); };
                Invoke(method);

            }
            else
            {
                ActiveNodeIndex = ExpandAllParents(value);
                this.Update();
            }
        }

        /// <summary>
        /// Gets or sets the index of the active node.
        /// </summary>
        /// <value>The index of the active node.</value>
        /// <exception cref="OutOfMemoryException">ActiveNodeIndex</exception>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int ActiveNodeIndex
        {
            get
            {
                return _activeNodeIndex;
            }

            set 
            {
                SetActiveNodeIndexInternal(value);
            }
        }

        private void SetActiveNodeIndexInternal(int value)
        {
            if (InvokeRequired)
            {
                MethodInvoker method = delegate { SetActiveNodeIndexInternal(value); };
                Invoke(method);
            }
            else
            {
                if (Items.Count <= value)
                {
                    throw new OutOfMemoryException("ActiveNodeIndex"); //MLHIDE
                }

                SelectedIndicesClearInternal();
                if (value != -1)
                {
                    _setActiveNodeIndexLock++;
                    try
                    {
                        // force creating control handle
                        if (!IsHandleCreated)
                        {
                            IntPtr handle = Handle;
                            Thread.Sleep(0);
                        }

                        ListViewItem item = Items[value];
                        item.Focused = true;
                        item.Selected = true;
                        SelectedIndices.Add(value);
                        item.EnsureVisible();

                        //FocusedItem = item;
                        //Items[value].Focused = true;
                        //Items[value].Selected = true;
                    }
                    finally
                    {
                        _setActiveNodeIndexLock--;
                    }
                }
                else
                {
                    if (base.FocusedItem != null)
                    {
                        base.FocusedItem.Focused = false;
                    }
                    else
                    {
                        base.FocusedItem = base.FocusedItem;
                    }

                    _activeNode = null;
                }

                _activeNodeIndex = value;
                if (value != -1)
                {
                    OnSelectedIndexChanged(EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// Формат текста ячейки по умолчанию.
        /// </summary>
        /// <value>The default text format.</value>
        [Editor(typeof (FlagsEditor), typeof (UITypeEditor)), DefaultValue(0x8004)]
        public TextFormatFlags DefaultTextFormat
        {
            get { return _defaultTextFormat; }
            set
            {
                _defaultTextFormat = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// Gets or sets the indent.
        /// </summary>
        /// <value>The indent.</value>
        public int Indent
        {
            get { return _indent; }
            set
            {
                _indent = value;
                Refresh();
            }
        }

        /// <summary>
        /// Gets the <see cref="Rsdn.TreeGrid.ITreeNode"/> at the specified index.
        /// </summary>
        /// <value></value>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ITreeNode this[int index]
        {
            get { return _displayNodes[index]; }
        }

        /// <summary>
        /// Gets or sets the nodes.
        /// </summary>
        /// <value>The nodes.</value>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ITreeNode Nodes
        {
            get
            {
                return _root;
            }

            set 
            {
                SetNodesInternal(value);
            }
        }

        private void SetNodesInternal(ITreeNode value)
        {
            if (InvokeRequired)
            {
                MethodInvoker method = delegate { SetNodesInternal(value); };
                Invoke(method);
            }
            else
            {
                this.BeginUpdate();
#if ALEX_CORRECTION
                if ((_root != null) && (_root.Count > 0) && Items.Count > 0)
#else
                if (_root != null && _root.Count > 0)
#endif
                {
                    EnsureVisible(0);
                }

                _scrollOnSetItemCount = true;
                try
                {
                    ActiveNodeIndex = -1;
                    _displayNodes.Clear();
                    _root = value;
                    if (_root != null)
                    {
                        ExpandNode(-1, _root);
                        _root.Flags |= NodeFlags.Expanded;
                    }
                }
                finally
                {
                    _scrollOnSetItemCount = false;
                    EndUpdate();
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the <see cref="T:System.Windows.Forms.ListView"/> control is drawn by the operating system or by code that you provide.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Windows.Forms.ListView"/> control is drawn by code that you provide; false if the <see cref="T:System.Windows.Forms.ListView"/> control is drawn by the operating system. The default is false.
        /// </returns>
        /// <PermissionSet>
        /// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// </PermissionSet>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        protected new bool OwnerDraw
        {
            get { return base.OwnerDraw; }
            set { throw new InvalidExpressionException(ml.ml_string(1, "Set OwnerDraw недоступен в TreeGrid")); }
        }

        /// <summary>
        /// Gets the selected nodes.
        /// </summary>
        /// <value>The selected nodes.</value>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public List<ITreeNode> SelectedNodes
        {
            get
            {
                return GetSelectedNodesInteral();
            }
        }

        private List<ITreeNode> GetSelectedNodesInteral()
        {
            if (InvokeRequired)
            {
                return (List<ITreeNode>)Invoke(
                  new Func<List<ITreeNode>>(() => GetSelectedNodesInteral())
                );
            }
            else
            {
                int count = SelectedIndices.Count;
                int[] dest = new int[count];
                SelectedIndices.CopyTo(dest, 0);
                List<ITreeNode> list = new List<ITreeNode>(count);
                for (int i = 0; i < dest.Length; i++)
                {
                    int index = dest[i];
                    ITreeNode displayNode = _displayNodes[index];
                    list.Add(displayNode);
                }
                return list;
            }
        }

        /// <summary>
        /// Индекс колонки в которой отображается дерево.
        /// </summary>
        /// <value>The index of the tree column.</value>
        [DefaultValue(0)]
        public int TreeColumnIndex
        {
            get { return _treeColumnIndex; }
            set { _treeColumnIndex = value; }
        }

        /// <summary>
        /// Gets or sets how items are displayed in the control.
        /// </summary>
        /// <value></value>
        /// <returns>
        /// One of the <see cref="T:System.Windows.Forms.View"/> values. The default is <see cref="F:System.Windows.Forms.View.LargeIcon"/>.
        /// </returns>
        /// <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">
        /// The value specified is not one of the <see cref="T:System.Windows.Forms.View"/> values.
        /// </exception>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        protected new View View
        {
            get { return base.View; }
            set { throw new InvalidExpressionException(ml.ml_string(2, "View недоступен в TreeGrid.")); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether you have provided your own data-management operations for the <see cref="T:System.Windows.Forms.ListView"/> control.
        /// </summary>
        /// <value></value>
        /// <returns>true if <see cref="T:System.Windows.Forms.ListView"/> uses data-management operations that you provide; otherwise, false. The default is false.
        /// </returns>
        /// <exception cref="T:System.InvalidOperationException">
        ///   <see cref="P:System.Windows.Forms.ListView.VirtualMode"/> is set to true and one of the following conditions exist:
        /// <see cref="P:System.Windows.Forms.ListView.VirtualListSize"/> is greater than 0 and <see cref="E:System.Windows.Forms.ListView.RetrieveVirtualItem"/> is not handled.
        /// -or-
        /// <see cref="P:System.Windows.Forms.ListView.Items"/>, <see cref="P:System.Windows.Forms.ListView.CheckedItems"/>, or <see cref="P:System.Windows.Forms.ListView.SelectedItems"/> contains items.
        /// -or-
        /// Edits are made to <see cref="P:System.Windows.Forms.ListView.Items"/>.
        /// </exception>
        /// <PermissionSet>
        ///     <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        ///     <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        ///     <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
        ///     <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// </PermissionSet>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        protected new bool VirtualMode
        {
            get { return base.VirtualMode; }
            set { throw new InvalidExpressionException(ml.ml_string(3, "VirtualMode недоступен в TreeGrid.")); }
        }

        public event AfterActivateNodeHandler AfterActivateNode;

        public event AfterCollapseHandler AfterCollapse;

        public event AfterExpandHandler AfterExpand;

        public event BeforeCollapseHandler BeforeCollapse;

        public event BeforeExpandHandler BeforeExpand;

        public event EventHandler<GetDataEventArgs> GetData;

        /// <summary>
        /// Displays the nodes.
        /// </summary>
        /// <param name="insertIndex">Index of the insert.</param>
        /// <param name="nodes">The nodes.</param>
        private void DisplayNodes(int insertIndex, ICollection<ITreeNode> nodes)
        {
            lock (lockInsertNodes)
            {
                bool flag = _displayNodes.Count == 0;
                _displayNodes.InsertRange(insertIndex, nodes);
                VirtualListSize = _displayNodes.Count;
                if ((_activeNodeIndex >= insertIndex) && !flag)
                {
                    SetActiveNodeIndex(_activeNodeIndex + nodes.Count);
                }
            }
        }

        /// <summary>
        /// Ensures the visible.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="fPartialOK">if set to <c>true</c> [f partial OK].</param>
        public void EnsureVisible(ITreeNode node, bool fPartialOK)
        {
            int index = ExpandAllParents(node);
            if (index >= 0)
            {
                this.EnsureVisible(index);
            }
        }

        /// <summary>
        /// Expands this instance.
        /// </summary>
        private void Expand()
        {
            OnBeforeExpand(_activeNode, ActiveNodeIndex);
        }

        /// <summary>
        /// Expands all parents.
        /// </summary>
        /// <param name="CurNode">The cur node.</param>
        /// <exception cref="Exception"></exception>
        /// <returns></returns>
        protected int ExpandAllParents(ITreeNode CurNode)
        {
            if (CurNode == null)
            {
                return -1;
            }

            if (CurNode.Parent == null)
            {
                int nodeIndex = GetNodeIndex(CurNode);
                if (nodeIndex < 0)
                {
                    throw new Exception(ml.ml_string(4, "Строка не подключена к TreGrid!"));
                }

                return nodeIndex;
            }

            if ((CurNode.Flags & NodeFlags.Expanded) == NodeFlags.None)
            {
                ITreeNode parent = CurNode.Parent;
                while ((parent.Flags & NodeFlags.Expanded) == NodeFlags.None)
                {
                    parent.Flags |= NodeFlags.AutoExpand;
                    if (((parent.Parent == _root)
                         || (parent.Parent == null))
                        || ((parent.Parent.Flags & NodeFlags.Expanded) != NodeFlags.None))
                    {
                        break;
                    }

                    parent = parent.Parent;
                }

                if ((parent.Flags & NodeFlags.Expanded) != NodeFlags.None)
                {
                    return GetNodeIndex(CurNode);
                }

                // Note: AN Added if
                if (parent.Parent != null)
                {

                    int iInsertIndex = GetNodeIndex(parent);
                    if (iInsertIndex < 0)
                    {
                        throw new Exception(ml.ml_string(4, "Строка не подключена к TreGrid!"));
                    }

                    ExpandNode(iInsertIndex, parent);
                }
                else
                {
                    int a = 0;
                }
            }

            return GetNodeIndex(CurNode);
        }

        /// <summary>
        /// Expands the node.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <exception cref="Exception">Нельзя разворачивать скрытую ветку.</exception>
        public void ExpandNode(ITreeNode node)
        {
            if (((node.Flags & NodeFlags.Expanded) == NodeFlags.None) && node.HasChildren)
            {
                int nodeIndex = GetNodeIndex(node);
                if (nodeIndex < 0)
                {
                    throw new Exception(ml.ml_string(5, "Нельзя разворачивать скрытую ветку."));
                }

                ExpandNode(nodeIndex, node);
            }
        }

        /// <summary>
        /// Expands the node.
        /// </summary>
        /// <param name="iInsertIndex">Index of the i insert.</param>
        /// <param name="node">The node.</param>
        private void ExpandNode(int iInsertIndex, ITreeNode node)
        {
            List<ITreeNode> nodes = new List<ITreeNode>(0x3e8);
            GetExpandNodeList(node, nodes);
            DisplayNodes(iInsertIndex + 1, nodes);
            if (nodes.Count > 0)
            {
                node.Flags |= NodeFlags.Expanded;
            }
        }

        /// <summary>
        /// Expands the node all.
        /// </summary>
        /// <param name="node">The node.</param>
        public void ExpandNodeAll(ITreeNode node)
        {
            SetSubNodesAutoExpand(node);
            ExpandNode(node);
        }

        /// <summary>
        /// Gets the cell pos.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <returns></returns>
        private CellPosition GetCellPos(int x, int y)
        {
            CellPosition position;
            LVHITTESTINFO lvhittestinfo2 = new LVHITTESTINFO();
            lvhittestinfo2.pt.X = x;
            lvhittestinfo2.pt.Y = y;
            LVHITTESTINFO lvhittestinfo = lvhittestinfo2;
            position.Row = SendMessage(Handle, Messages.LVM_SUBITEMHITTEST, 0, ref lvhittestinfo);
            if (position.Row < 0)
            {
                position.Column = -1;
                return position;
            }

            position.Column = lvhittestinfo.iSubItem;
            return position;
        }

        /// <summary>
        /// Gets the cell rect.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="col">The col.</param>
        /// <returns></returns>
        /// <exception cref="Exception">Нельзя получить координаты ячейки так как количество строк равно нулю.</exception>
        private Rectangle GetCellRect(int row, int col)
        {
            int count = Columns.Count;
            int num2 = VirtualListSize;
            if (num2 == 0)
            {
                throw new Exception(ml.ml_string(6, "Нельзя получить координаты ячейки так как количество строк равно нулю."));
            }

            if ((row >= num2) || (row < 0))
            {
                throw new Exception(ml.ml_string(7, "Неверный номер строки."));
            }

            Rectangle itemRect = GetItemRect(row, ItemBoundsPortion.Entire);
            int index = 0;
            ColumnHeader[] headerArray = new ColumnHeader[Columns.Count];
            foreach (ColumnHeader header in Columns)
            {
                headerArray[header.DisplayIndex] = header;
            }

            while ((index < count) && (headerArray[index].Index != col))
            {
                itemRect.X += headerArray[index].Width;
                index++;
            }

            itemRect.Width = headerArray[index].Width;
            if (GridLines)
            {
                itemRect.Height -= 2;
            }
            else
            {
                itemRect.Height--;
            }

            itemRect.X++;
            return itemRect;
        }

        /// <summary>
        ///   Gets the expanded node count.
        /// </summary>
        /// <param name = "node">The node.</param>
        /// <returns></returns>
        private static int GetExpandedNodeCount(ITreeNode node)
        {
            int count = node.Count;
            int num2 = count;
            for (int i = 0; i < count; i++)
            {
                ITreeNode node2 = node[i];
                if ((node2.Flags & NodeFlags.Expanded) != NodeFlags.None)
                {
                    num2 += GetExpandedNodeCount(node2);
                    node2.Flags &= ~NodeFlags.Expanded;
                }
            }

            return num2;
        }

        /// <summary>
        ///   Gets the expand node list.
        /// </summary>
        /// <param name = "node">The node.</param>
        /// <param name = "nodes">The nodes.</param>
        private static void GetExpandNodeList(ITreeNode node, ICollection<ITreeNode> nodes)
        {
            int count = node.Count;
            for (int i = 0; i < count; i++)
            {
                ITreeNode item = node[i];
                nodes.Add(item);
                if ((item.Flags & NodeFlags.AutoExpand) != NodeFlags.None)
                {
                    item.Flags |= NodeFlags.Expanded;
                    GetExpandNodeList(item, nodes);
                }
            }
        }

        /// <summary>
        ///   Рассчитывает количество раскрытых подветок. Учитывая вложенные.
        /// </summary>
        /// <param name = "node">Ветка для которой нужно произвести рассчет.</param>
        /// <returns>Количество расскрытых подветок.</returns>
        private static int GetExpandSubNodeCount(ITreeNode node)
        {
            if ((node.Flags & NodeFlags.Expanded) == NodeFlags.None)
            {
                return 0;
            }

            int count = node.Count;
            int num2 = count;
            for (int i = 0; i < count; i++)
            {
                ITreeNode node2 = node[i];
                if ((node2.Flags & NodeFlags.Expanded) != NodeFlags.None)
                {
                    num2 += GetExpandSubNodeCount(node2);
                }
            }

            return num2;
        }

        /// <summary>
        ///   Gets the flat array of sub nodes.
        /// </summary>
        /// <param name = "root">The root.</param>
        /// <returns></returns>
        public static ITreeNode[] GetFlatArrayOfSubNodes(ITreeNode root)
        {
            List<ITreeNode> list2 = new List<ITreeNode>(100);
            list2.Add(root);
            List<ITreeNode> nodes = list2;
            GetFlatArrayOfSubNodes(nodes, root);
            return nodes.ToArray();
        }

        /// <summary>
        ///   Gets the flat array of sub nodes.
        /// </summary>
        /// <param name = "root">The root.</param>
        /// <param name = "startBeyond">The start beyond.</param>
        /// <returns></returns>
        public static ITreeNode[] GetFlatArrayOfSubNodes(ITreeNode root, ITreeNode startBeyond)
        {
            return GetFlatArrayOfSubNodes(root, startBeyond, false);
        }

        /// <summary>
        ///   Gets the flat array of sub nodes.
        /// </summary>
        /// <param name = "nodes">The nodes.</param>
        /// <param name = "curr">The curr.</param>
        protected static void GetFlatArrayOfSubNodes(List<ITreeNode> nodes, ITreeNode curr)
        {
            if (curr.HasChildren)
            {
                foreach (ITreeNode node in curr)
                {
                    nodes.Add(node);
                    GetFlatArrayOfSubNodes(nodes, node);
                }
            }
        }

        /// <summary>
        ///   Gets the flat array of sub nodes.
        /// </summary>
        /// <param name = "root">The root.</param>
        /// <param name = "startBeyond">The start beyond.</param>
        /// <param name = "isReverse">if set to <c>true</c> [is reverse].</param>
        /// <returns></returns>
        /// <exception cref="ApplicationException">GetFlatArrayOfSubNodes: Этого не должно было произойти. VladD2 мыло с описанием в какой ситуации произошло данное исключение</exception>
        public static ITreeNode[] GetFlatArrayOfSubNodes(ITreeNode root, ITreeNode startBeyond, bool isReverse)
        {
            List<ITreeNode> list2 = new List<ITreeNode>(100);
            list2.Add(root);
            List<ITreeNode> nodes = list2;
            GetFlatArrayOfSubNodes(nodes, root);
            int index = 0;
            if (startBeyond != null)
            {
                index = isReverse ? nodes.LastIndexOf(startBeyond) : nodes.IndexOf(startBeyond);
                if (index < 0)
                {
                    throw new ApplicationException(ml.ml_string(8, "GetFlatArrayOfSubNodes: Неверно задан аргумент startBeyond."));
                }

                if (!isReverse)
                {
                    index++;
                }
            }

            int count = isReverse ? index : (nodes.Count - index);
            if (count < 0)
            {
                //GetFlatArrayOfSubNodes: Этого не должно было произойти. VladD2 мыло с описанием в какой ситуации произошло данное исключение
                throw new ApplicationException(
                    ml.ml_string(9, "GetFlatArrayOfSubNodes: Несовпадение индекса и общего количества строк"));
            }

            ITreeNode[] array = new ITreeNode[count];
            if (isReverse)
            {
                nodes.CopyTo(0, array, 0, count);
                Array.Reverse(array);
                return array;
            }

            nodes.CopyTo(index, array, 0, count);
            return array;
        }

        /// <summary>
        ///   Gets the flat array of sub nodes reverse.
        /// </summary>
        /// <param name = "root">The root.</param>
        /// <param name = "startBeyond">The start beyond.</param>
        /// <returns></returns>
        public static ITreeNode[] GetFlatArrayOfSubNodesReverse(ITreeNode root, ITreeNode startBeyond)
        {
            return GetFlatArrayOfSubNodes(root, startBeyond, true);
        }

        /// <summary>
        ///   Gets the node and cell index at.
        /// </summary>
        /// <param name = "x">The x.</param>
        /// <param name = "y">The y.</param>
        /// <param name = "node">The node.</param>
        /// <param name = "column">The column.</param>
        public void GetNodeAndCellIndexAt(int x, int y, out ITreeNode node, out int column)
        {
            CellPosition cellPos = GetCellPos(x, y);
            node = (cellPos.Row < 0) ? null : _displayNodes[cellPos.Row];
            column = cellPos.Column;
        }

        /// <summary>
        ///   Gets the index of the node.
        /// </summary>
        /// <param name = "node">The node.</param>
        /// <returns></returns>
        private int GetNodeIndex(ITreeNode node)
        {
            int nodeIndex = -1;

            lock (lockInsertNodes)
            {
                //int nodeIndex = _displayNodes.IndexOf(node); // sometimes return wrong result. When display nodes != actual node

                for (int i = 0; i < _displayNodes.Count; i++)
                {
                    ITreeNode displayNode = _displayNodes[i];
                    // displayNode = {.NET GUI (сообщений: 1351, не прочитанных: 0, ответов мне: 0)}
                    // node = {.NET GUI (сообщений: 1146, не прочитанных: 0, ответов мне: 0)}
                    if (displayNode.Equals(node))
                    {
                        nodeIndex = i;
                        break;
                    }
                }
            }
            return nodeIndex;
        }

        /// <summary>
        ///   Gets the node level.
        /// </summary>
        /// <param name = "node">The node.</param>
        /// <returns></returns>
        private static int GetNodeLevel(ITreeNode node)
        {
            int num = 0;
            while (node.Parent != null)
            {
                node = node.Parent;
                num++;
            }

            return num;
        }

        /// <summary>
        ///   Hides the display nodes.
        /// </summary>
        /// <param name = "nodeDisplayIndex">Display index of the node.</param>
        /// <param name = "count">The count.</param>
        private void HideDisplayNodes(int nodeDisplayIndex, int count)
        {
            _displayNodes.RemoveRange(nodeDisplayIndex, count);
            VirtualListSize = _displayNodes.Count;
            int num = nodeDisplayIndex + count;
            if (ActiveNodeIndex >= nodeDisplayIndex)
            {
                if (ActiveNodeIndex < num)
                {
                    if (nodeDisplayIndex == 0)
                    {
                        SetActiveNodeIndex(0);
                    }
                    else
                    {
                        SetActiveNodeIndex(nodeDisplayIndex - 1);
                    }
                }
                else
                {
                    SetActiveNodeIndex(ActiveNodeIndex - count);
                }
            }

            if (_displayNodes.Count == 0)
            {
                _activeNodeIndex = -1;
                this.Update();
            }
        }

        /// <summary>
        /// Inits the highlight font.
        /// </summary>
        private void InitHighlightFont()
        {
            if (_highlightFont != null)
            {
                _highlightFont.Dispose();
            }

            _highlightFont = new Font(Font, Font.Style | FontStyle.Bold);
        }


        /// <summary>
        /// Initializes the renderers.
        /// </summary>
        private void InitializeRenderers()
        {
            if (_visualStylePlusRenderer == null)
            {
                _visualStylePlusRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Closed);
                _visualStyleMinusRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Opened);
            }
        }

        /// <summary>
        /// Lefts the key down.
        /// </summary>
        protected void LeftKeyDown()
        {
            if ((_activeNode.Flags & NodeFlags.Expanded) == NodeFlags.None)
            {
                if (GetNodeLevel(_activeNode) > 1)
                {
                    ActiveNode = ActiveNode.Parent;
                }
            }
            else
            {
                OnBeforeCollapse(_activeNode, ActiveNodeIndex);
            }
        }

        /// <summary>
        /// Позволяет оповестить о динамически добавленной ветке. Если необходимо
        /// произвести массовые модификации коллекции, лучше отключить коллекцию
        /// от TreeGrid, изменить ее и подключить обратно.
        /// Если нужно добавить несколько веток к одному родителю, лучше
        /// воспользоваться методом NotifyAddNodes.
        /// </summary>
        /// <param name="node">Добавляемая ветка.</param>
        /// <param name="insertIndex">Индекс в родительской ветке по которому
        /// будет производиться вставка.</param>
        public void NotifyAddNode(ITreeNode node, int insertIndex)
        {
            List<ITreeNode> list2 = new List<ITreeNode>(1);
            list2.Add(node);
            List<ITreeNode> newNodes = list2;
            NotifyAddNodes(node.Parent, newNodes, insertIndex);
        }

        /// <summary>
        /// Позволяет оповестить о динамическом добавлении нескольких ветке к
        /// одной родительской. Если необходимо произвести массовые модификации
        /// коллекции, лучше отключить коллекцию от TreeGrid, изменить ее и
        /// подключить обратно.
        /// Если нужно добавить одну ветку воспользуйтесь методом NotifyAddNode.
        /// </summary>
        /// <param name="parent">Родительская ветка.</param>
        /// <param name="newNodes">Массив содержаций добавляемые ветки.</param>
        /// <param name="insertIndex">Индекс в родительской ветке по которому
        /// будет производиться вставка.</param>
        /// <exception cref="ArgumentNullException"><paramref name="parent" /> is <c>null</c>.</exception>
        public void NotifyAddNodes(ITreeNode parent, List<ITreeNode> newNodes, int insertIndex)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent");            //MLHIDE
            }

            if ((parent.Flags & NodeFlags.Expanded) != NodeFlags.None)
            {
                int num;
                if (insertIndex > 0)
                {
                    ITreeNode node = parent[insertIndex - 1];
                    num = (GetExpandSubNodeCount(node) + GetNodeIndex(node)) + 1;
                }
                else if (Nodes == parent)
                {
                    num = 0;
                }
                else
                {
                    num = GetNodeIndex(parent) + 1;
                }

                List<ITreeNode> nodes = new List<ITreeNode>(0x3e8);
                foreach (ITreeNode node2 in newNodes)
                {
                    nodes.Add(node2);
                    GetExpandNodeList(node2, nodes);
                }

                DisplayNodes(num, nodes);
                this.Update();
            }
            else if (parent.Parent != null)
            {
                int nodeIndex = GetNodeIndex(parent);
                this.RedrawItems(nodeIndex, nodeIndex, false);
            }
        }

        /// <summary>
        /// Notifies the remove node.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <exception cref="ArgumentNullException"><paramref name="node" /> is <c>null</c>.</exception>
        public void NotifyRemoveNode(ITreeNode node)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");              //MLHIDE
            }

            if ((node.Parent.Flags & NodeFlags.Expanded) != NodeFlags.None)
            {
                int count = GetExpandSubNodeCount(node) + 1;
                HideDisplayNodes(GetNodeIndex(node), count);
                this.Update();
            }
        }

        /// <summary>
        /// Notifies the remove nodes.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="delIndex">Index of the i del.</param>
        /// <param name="count">The count.</param>
        /// <exception cref="ArgumentNullException"><paramref name="parent" /> is <c>null</c>.</exception>
        public void NotifyRemoveNodes(ITreeNode parent, int delIndex, int count)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent");            //MLHIDE
            }

            if ((parent.Flags & NodeFlags.Expanded) != NodeFlags.None)
            {
                int num = count;
                for (int i = delIndex; i < count; i++)
                {
                    num += GetExpandSubNodeCount(parent[i]);
                }

                HideDisplayNodes(GetNodeIndex(parent[delIndex]), num);
                this.Update();
            }
        }

        /// <summary>
        /// Called when [after activate node].
        /// </summary>
        /// <param name="activatedNode">The activated node.</param>
        protected virtual void OnAfterActivateNode(ITreeNode activatedNode)
        {
            if (AfterActivateNode != null)
            {
                AfterActivateNode(activatedNode);
            }
        }

        /// <summary>
        /// Called when [before collapse].
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="nodeIndex">The i node.</param>
        protected virtual void OnBeforeCollapse(ITreeNode node, int nodeIndex)
        {
            if ((node.Flags & NodeFlags.Expanded) != NodeFlags.None)
            {
                if (BeforeCollapse != null)
                {
                    bool isCancel = false;
                    BeforeCollapse(this, node, nodeIndex, ref isCancel);
                    if (isCancel)
                    {
                        return;
                    }
                }

                BeginUpdate();
                try
                {
                    CollapseNode(nodeIndex, node);
                    node.Flags &= ~NodeFlags.AutoExpand;
                    if (AfterCollapse != null)
                    {
                        AfterCollapse(this, node, nodeIndex);
                    }

                    Update();
                }
                finally
                {
                    EndUpdate();
                }
            }
        }

        /// <summary>
        /// Called when [before expand].
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="nodeIndex">The i node.</param>
        protected virtual void OnBeforeExpand(ITreeNode node, int nodeIndex)
        {
            if (node.HasChildren && ((node.Flags & NodeFlags.Expanded) == NodeFlags.None))
            {
                if (BeforeExpand != null)
                {
                    bool isCancel = false;
                    BeforeExpand(this, node, nodeIndex, ref isCancel);
                    if (isCancel)
                    {
                        return;
                    }
                }

                BeginUpdate();
                try
                {
                    ExpandNode(nodeIndex, node);
                    node.Flags |= NodeFlags.AutoExpand;
                    if (AfterExpand != null)
                    {
                        AfterExpand(this, node, nodeIndex);
                    }

                    Update();
                }
                finally
                {
                    EndUpdate();
                }
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.ListView.DrawColumnHeader"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DrawListViewColumnHeaderEventArgs"/> that contains the event data.</param>
        protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
        {
            e.DrawDefault = true;
            base.OnDrawColumnHeader(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.ListView.DrawItem"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DrawListViewItemEventArgs"/> that contains the event data.</param>
        protected override void OnDrawItem(DrawListViewItemEventArgs e)
        {
            e.DrawDefault = false;
            try
            {
                int count = Columns.Count;
                if (count > 0)
                {
                    int index = e.ItemIndex;
                    if (_highlightFont == null)
                    {
                        InitHighlightFont();
                    }

                    if (_displayNodes.Count > 0)
                    {
                        ITreeNode node = _displayNodes[index];
                        NodeDrawData data = RetrieveData(node);

                        if (e.Item.Selected)
                        {
                            Color color = data.NodeInfo.ForeColor;
                            data.NodeInfo.ForeColor = data.NodeInfo.BackColor;
                            data.NodeInfo.BackColor = color;
                        }

                        Font font = data.NodeInfo.Highlight ? _highlightFont : Font;
                        Graphics g = e.Graphics;

                        if (Enabled)
                        {
                            if (_paintCache != null)
                            {
                                _paintCache.FillSolidRect(
                                    g, 
                                    GetItemRect(index, ItemBoundsPortion.Entire), 
                                    data.NodeInfo.BackColor);
                            }
                        }

                        Color foreColor = Enabled ? data.NodeInfo.ForeColor : SystemColors.ControlDark;

                        for (int i = 0; i < count; i++)
                        {
                            try
                            {
                                Image image;
                                CellInfo info = data.CellInfos[i];
                                Rectangle cellRect = GetCellRect(index, i);
                                g.SetClip(cellRect);
                                TextFormatFlags textFormat = info.TextFormat;
                                string text = info.Text;
                                if (i == _treeColumnIndex)
                                {
                                    int num4 = GetNodeLevel(node) * Indent;
                                    Rectangle rectangle2 = cellRect;
                                    rectangle2.X += 4 + num4;
                                    rectangle2.Width = _plusMinusImages.ImageSize.Width;
                                    rectangle2.Height = _plusMinusImages.ImageSize.Height;
                                    rectangle2.Y += (cellRect.Height - _plusMinusImages.ImageSize.Height) / 2;
                                    cellRect.Width -= rectangle2.Right - cellRect.X;
                                    cellRect.X = rectangle2.Right;
                                    if (node.HasChildren)
                                    {
                                        if (VisualStyleRenderer.IsSupported)
                                        {
                                            InitializeRenderers();
                                            (((node.Flags & NodeFlags.Expanded) == NodeFlags.None)
                                                 ? _visualStylePlusRenderer
                                                 : _visualStyleMinusRenderer).DrawBackground(
                                                     e.Graphics, rectangle2);
                                        }
                                        else
                                        {
                                            _plusMinusImages.Draw(
                                                g, 
                                                rectangle2.Location, 
                                                ((node.Flags & NodeFlags.Expanded) == NodeFlags.None) ? 1 : 0);
                                        }
                                    }
                                }

                                switch (info.CellImageType)
                                {
                                    case CellImageType.Index:
                                        image = ((SmallImageList != null) && (info.ImageIndex >= 0))
                                                    ? SmallImageList.Images[info.ImageIndex]
                                                    : null;
                                        break;

                                    case CellImageType.Key:
                                        image = ((SmallImageList != null) && !string.IsNullOrEmpty(info.ImageKey))
                                                    ? SmallImageList.Images[info.ImageKey]
                                                    : null;
                                        break;

                                    case CellImageType.Image:
                                        image = info.Image;
                                        break;

                                    default:
                                        image = null;
                                        break;
                                }

                                if (image != null)
                                {
                                    Rectangle rectangle3 = cellRect;
                                    rectangle3.X += 4;
                                    rectangle3.Width = image.Width;
                                    rectangle3.Y += (rectangle3.Height - image.Height) / 2;
                                    if (rectangle3.Height > image.Height)
                                    {
                                        rectangle3.Height = image.Height;
                                    }

                                    cellRect.Width -= rectangle3.Right - cellRect.X;
                                    cellRect.X = rectangle3.Right;
                                    g.DrawImageUnscaledAndClipped(image, rectangle3);
                                }

                                cellRect.X += 4;
                                cellRect.Width -= 4;
                                cellRect.Y++;
                                if (_paintCache != null)
                                {
                                    _paintCache.DrawText(
                                        g, text, font, foreColor, cellRect, textFormat);
                                }
                            }
                            catch (Exception exception)
                            {
                                Trace.WriteLine(exception);
                            }
                        }

                        if (e.Item.Focused)
                        {
                            Rectangle rectangle = e.Bounds;
                            if (GridLines)
                            {
                                rectangle.Height--;
                            }

                            ControlPaint.DrawFocusRectangle(g, rectangle, foreColor, data.NodeInfo.BackColor);
                        }
                    }
                }
            }
            catch (Exception exception2)
            {
                Trace.WriteLine(exception2);
            }
        }

        protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
        {
        }

        /// <summary>
        /// Raises the FontChanged event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            InitHighlightFont();
        }

        /// <summary>
        /// Raises the <see cref="E:GetData"/> event.
        /// </summary>
        /// <param name="e">The <see cref="Rsdn.TreeGrid.GetDataEventArgs"/> instance containing the event data.</param>
        /// <returns></returns>
        protected virtual bool OnGetData(GetDataEventArgs e)
        {
            if (GetData == null)
            {
                return false;
            }

            GetData(this, e);
            return true;
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.KeyDown"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.KeyEventArgs"/> that contains the event data.</param>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            if (!e.Handled)
            {
                switch (e.KeyData)
                {
                    case Keys.Left:
                        LeftKeyDown();
                        e.Handled = true;
                        return;

                    case Keys.Up:
                    case Keys.Separator:
                        return;

                    case Keys.Right:
                        RightKeyDown();
                        e.Handled = true;
                        return;

                    case Keys.Multiply:
                        if ((ActiveNode != null) && ActiveNode.HasChildren)
                        {
                            ExpandNodeAll(ActiveNode);
                        }

                        e.Handled = true;
                        return;

                    case Keys.Add:
                        Expand();
                        e.Handled = true;
                        return;

                    case Keys.Subtract:
                        Collapse();
                        e.Handled = true;
                        return;
                }
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.ListView.RetrieveVirtualItem"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.RetrieveVirtualItemEventArgs"/> that contains the event data.</param>
        protected override void OnRetrieveVirtualItem(RetrieveVirtualItemEventArgs e)
        {
            ListViewItem item = new ListViewItem();
            if (item.SubItems.Count != Columns.Count)
            {
                ListViewItem item2 = new ListViewItem();
                item2.Text = string.Empty;
                item = item2;
                int num = 1;
                int count = Columns.Count;
                while (num < count)
                {
                    item.SubItems.Add(string.Empty);
                    num++;
                }
            }

            e.Item = item;
        }

        /// <summary>
        /// Collapses this instance.
        /// </summary>
        private void Collapse()
        {
            OnBeforeCollapse(_activeNode, ActiveNodeIndex);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.ListView.SelectedIndexChanged"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnSelectedIndexChanged(EventArgs e)
        {
            if (_setActiveNodeIndexLock == 0)
            {
                base.OnSelectedIndexChanged(e);
                _uiOperation.Post(
                    delegate { PostSelectedIndexChanged(); }, 
                    null);
            }
        }

        /// <summary>
        /// Posts the selected index changed.
        /// </summary>
        private void PostSelectedIndexChanged()
        {
            ListViewItem focusedItem = FocusedItem;
            if (focusedItem == null)
            {
                if (_activeNode != null)
                {
                    _activeNode.Flags &= ~NodeFlags.Active;
                }

                _activeNode = null;
                _activeNodeIndex = -1;
            }
            else if ((_activeNodeIndex != focusedItem.Index)
                     || (_activeNode != _displayNodes[_activeNodeIndex]))
            {
                _activeNodeIndex = focusedItem.Index;
                _activeNode = _displayNodes[_activeNodeIndex];
                _activeNode.Flags |= NodeFlags.Active;
                OnAfterActivateNode(_activeNode);
            }
        }

        /// <summary>
        /// Производит извлечение данных отрисовки из узла.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <returns></returns>
        protected virtual NodeDrawData RetrieveData(ITreeNode node)
        {
            CellInfo[] cellInfos = new CellInfo[base.Columns.Count];
            NodeInfo nodeInfo = new NodeInfo(ForeColor, BackColor, Font, false);
            for (int i = 0; i < cellInfos.Length; i++)
            {
                cellInfos[i].Init();
                cellInfos[i].TextFormat = DefaultTextFormat;
            }

            GetDataEventArgs e = new GetDataEventArgs(node, nodeInfo, cellInfos);
            if (!OnGetData(e))
            {
                IGetData data = node as IGetData;
                if (data != null)
                {
                    data.GetData(nodeInfo, cellInfos);
                }
            }

            return new NodeDrawData(nodeInfo, cellInfos);
        }

        /// <summary>
        /// Rights the key down.
        /// </summary>
        protected void RightKeyDown()
        {
            if (_activeNode.HasChildren)
            {
                if ((_activeNode.Flags & NodeFlags.Expanded) != NodeFlags.None)
                {
                    if (_activeNode.Count > 0)
                    {
                        ActiveNodeIndex++;
                    }
                }
                else
                {
                    OnBeforeExpand(_activeNode, ActiveNodeIndex);
                }
            }
        }

        /// <summary>
        /// Clear internal selected indices.
        /// </summary>
        private void SelectedIndicesClearInternal()
        {
            if (InvokeRequired)
            {
                MethodInvoker method = delegate { SelectedIndicesClearInternal(); };
                Invoke(method);
            }
            else
            {
                _setActiveNodeIndexLock++;
                try
                {
                    SelectedIndices.Clear();
                }
                finally
                {
                    _setActiveNodeIndexLock--;
                }
            }
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        protected static extern int SendMessage(IntPtr handle, Messages messg, int notUsed, ref LVHITTESTINFO pInfo);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        protected static extern bool SendMessage(IntPtr handle, int messg, int itemCount, SetItemCountFlags flags);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        protected static extern int SendMessage(IntPtr handle, int messg, int wparam, int lparam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        protected static extern bool SendMessage(IntPtr handle, int messg, int count, int[] array);

        /// <summary>
        /// Sets the index of the active node.
        /// </summary>
        /// <param name="value">The value.</param>
        private void SetActiveNodeIndex(int value)
        {
            int count = SelectedIndices.Count;
            SelectedIndicesClearInternal();
            _activeNodeIndex = value;
            if (value >= 0)
            {
                ListViewItem item = Items[ActiveNodeIndex];
                item.Focused = true;
                if (count == 1)
                {
                    item.Selected = true;
                }
            }
            else
            {
                _activeNode = null;
            }
        }

        /// <summary>
        ///   Sets ListViewExtended Styles
        /// </summary>
        /// <param name = "exStyle">The Styles you wish to set.</param>
        public void SetExStyles(LVS_EX exStyle)
        {
            LVS_EX lvs_ex = ((LVS_EX) SendMessage(Handle, 0x1037, 0, 0)) | exStyle;
            SendMessage(Handle, 0x1036, 0, (int) lvs_ex);
        }

        /// <summary>
        /// Sets the sub nodes auto expand.
        /// </summary>
        /// <param name="node">The current node.</param>
        protected static void SetSubNodesAutoExpand(ITreeNode node)
        {
            node.Flags |= NodeFlags.AutoExpand;
            foreach (ITreeNode node2 in node)
            {
                SetSubNodesAutoExpand(node2);
            }
        }

        /// <summary>
        /// Signeds the HIWORD.
        /// </summary>
        /// <param name="n">The n.</param>
        /// <returns></returns>
        public static int SignedHIWORD(int n)
        {
            return (n >> 0x10) & 0xffff;
        }

        /// <summary>
        /// Signeds the LOWORD.
        /// </summary>
        /// <param name="n">The n.</param>
        /// <returns></returns>
        public static int SignedLOWORD(int n)
        {
            return n & 0xffff;
        }

        /// <summary>
        /// Sets Double_Buffering and BorderSelect style
        /// </summary>
        public void TogleDoubleBuffer()
        {
            SetStyle((ControlStyles) 0x20000, !GetStyle((ControlStyles) 0x20000));
            LVS_EX lvs_ex = (LVS_EX) SendMessage(Handle, 0x1037, 0, 0);
            if ((lvs_ex & LVS_EX.LVS_EX_DOUBLEBUFFER) == 0)
            {
                lvs_ex |= LVS_EX.LVS_EX_DOUBLEBUFFER | LVS_EX.LVS_EX_BORDERSELECT;
            }
            else
            {
                lvs_ex &= ~(LVS_EX.LVS_EX_DOUBLEBUFFER | LVS_EX.LVS_EX_BORDERSELECT);
            }

            SendMessage(Handle, 0x1036, 0, (int) lvs_ex);
        }

        /// <summary>
        /// Overrides <see cref="M:System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message@)"/>.
        /// </summary>
        /// <param name="m">The Windows <see cref="T:System.Windows.Forms.Message"/> to process.</param>
        protected override void WndProc(ref Message m)
        {
            switch ((Messages) m.Msg)
            {
                case Messages.WM_PAINT:
                    using (_paintCache = new PaintCache())
                    {
                        base.WndProc(ref m);
                    }

                    _paintCache = null;
                    return;

                case Messages.WM_ERASEBKGND:
                    if (!this.GetStyle((ControlStyles) 0x20000))
                    {
                        break;
                    }

                    m.Result = (IntPtr) 1;
                    return;

                case Messages.LVM_SETITEMCOUNT:
                    if (!_scrollOnSetItemCount)
                    {
                        m.LParam = (IntPtr) 2L;
                    }

                    break;

                case Messages.WM_LBUTTONDOWN:
                case Messages.WM_LBUTTONDBLCLK:
                    {
                        Point pt = new Point(m.LParam.ToInt32());
                        ListViewHitTestInfo info = HitTest(pt);
                        if (info.Item == null)
                        {
                            break;
                        }

                        int index = info.Item.Index;
                        Rectangle cellRect = GetCellRect(index, _treeColumnIndex);
                        if (!cellRect.Contains(pt))
                        {
                            break;
                        }

                        ITreeNode node = _displayNodes[index];
                        int num2 = (GetNodeLevel(node) * Indent) + 4;
                        cellRect.X += num2;
                        cellRect.Width = _plusMinusImages.ImageSize.Width;
                        cellRect.Y += (cellRect.Height - _plusMinusImages.ImageSize.Height) / 2;
                        cellRect.Height = _plusMinusImages.ImageSize.Height;
                        if (!cellRect.Contains(pt) || !node.HasChildren)
                        {
                            break;
                        }

                        if ((node.Flags & NodeFlags.Expanded) != NodeFlags.None)
                        {
                            OnBeforeCollapse(node, index);
                            return;
                        }

                        OnBeforeExpand(node, index);
                        return;
                    }
            }

            base.WndProc(ref m);
        }

        /// <summary>
        /// Схлопывает ветку скрывая ее подветки в ListView.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="node">The node.</param>
        protected void CollapseNode(int index, ITreeNode node)
        {
            if ((node.Flags & NodeFlags.Expanded) == NodeFlags.None)
            {
                throw new Exception(ml.ml_string(10, "Нельзя сворачивать нераскрытую ветку."));
            }

            int hideNodeCount = GetExpandedNodeCount(node);
            int focusIndex = FocusedItem == null ? -1 : FocusedItem.Index;
            ListViewItem colabsedItem = Items[index];
            HideDisplayNodes(index + 1, hideNodeCount);
            node.Flags &= ~NodeFlags.Expanded;
            if (focusIndex >= 0 && focusIndex > index)
            {
                if (focusIndex < index + hideNodeCount)
                {
                    SelectedIndicesClearInternal();
                    colabsedItem.Focused = true;
                    colabsedItem.Selected = true;
                }
                else
                {
                    var selectedItem = Items[focusIndex - hideNodeCount];
                    selectedItem.Focused = true;
                    selectedItem.Selected = true;
                }
            }
        }

        /// <summary>
        /// Collapses the node.
        /// </summary>
        /// <param name="node">The node.</param>
        public void CollapseNode(ITreeNode node)
        {
            if (((node.Flags & NodeFlags.Expanded) != NodeFlags.None) && node.HasChildren)
            {
                int insertIndex = GetNodeIndex(node);
                if (insertIndex < 0)
                {
                    throw new Exception(ml.ml_string(11, "Нельзя сворачивать скрытую ветку."));
                }

                CollapseNode(insertIndex, node);
            }
        }

        #region Nested type: NodeDrawData

        protected class NodeDrawData
        {
            [CompilerGenerated] private CellInfo[] CellInfos_k__BackingField;
            [CompilerGenerated] private NodeInfo NodeInfo_k__BackingField;

            public NodeDrawData(NodeInfo nodeInfo, CellInfo[] cellInfos)
            {
                NodeInfo = nodeInfo;
                CellInfos = cellInfos;
            }

            public CellInfo[] CellInfos
            {
                [CompilerGenerated]
                get { return CellInfos_k__BackingField; }
                [CompilerGenerated]
                set { CellInfos_k__BackingField = value; }
            }

            public NodeInfo NodeInfo
            {
                [CompilerGenerated]
                get { return NodeInfo_k__BackingField; }
                [CompilerGenerated]
                set { NodeInfo_k__BackingField = value; }
            }
        }

        #endregion

        /// <summary>
        /// Selects all Nodes and items
        /// </summary>
        public void SelectAll()
        {
            SelectedIndicesClearInternal();
            try
            {
                _setActiveNodeIndexLock++;
                for (int i = 0; i < Items.Count; i++)
                {
                    ListViewItem item = Items[i];
                    item.Selected = true;
                    SelectedIndices.Add(i);
                }
            }
            finally
            {
                _setActiveNodeIndexLock--;
            }
        }
    }
}